﻿using PdfSharp.Pdf.IO;

namespace CSharpTest
{
    internal class PdfStreamContentsHelper
    {
        const string demo = "<0027004800500052>";//Demo
        const string demoVersion1 = "<002700480050005200030059004800550056004C00520051>";//Demo Version
        const string demoVersion2 = "<002700280030003200030039002800350036002C00320031>";//DEMO VERSION
        public PdfStreamContentsHelper(string filePath)
        {
            contentLines = new Lazy<IEnumerable<FilePageContentLine>>(() => Initialize(filePath));
        }

        static IEnumerable<FilePageContentLine> Initialize(string filePath)
        {
            List<FilePageContentLine> list = [];
            using var doc = PdfReader.Open(filePath);
            var pageIndex = 0;
            foreach (var page in doc.Pages)
            {
                var contentIndex = 0;
                foreach (var content in page.Contents)
                {
                    var lineIndex = 0;
                    var str = Encoding.UTF8.GetString(content.Stream.Value);
                    using var reader = new StringReader(str);
                    string? line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        list.Add(new FilePageContentLine
                        {
                            FilePath = filePath,
                            PageIndex = pageIndex,
                            ContentIndex = contentIndex,
                            LineIndex = lineIndex,
                            Text = line
                        });
                        lineIndex++;
                    }
                    contentIndex++;
                }
                pageIndex++;
            }
            return list;
        }
        /// <summary>
        /// 替换指定的行
        /// </summary>
        /// <param name="lines"></param>
        /// <param name="inputFilePath"></param>
        /// <param name="outFilePath"></param>
        /// <returns></returns>
        public static bool ReplaceLine(IEnumerable<PageContentLine> lines, string inputFilePath, string outFilePath)
        {
            // 多个字符串碰，尽量找合格的进行写入，一个都不合格也写入最后返回false
            static bool NoProblemAndWriteString(string[] inputs, TextWriter writer)
            {
                var success = false;
                foreach (var input in inputs)
                {
                    if (input.Contains(demo) || input.Contains(demoVersion1))
                    {
                        continue;
                    }
                    if (input.Contains(demoVersion2))
                    {
                        writer.WriteLine(input.Replace(demoVersion2, "0003"));
                        success = true;
                        break;
                    }
                    writer.WriteLine(input);
                    success = true;
                    break;
                }
                return success;
            }

            var perfect = true;
            using var doc = PdfReader.Open(inputFilePath);
            var pageIndex = 0;
            foreach (var page in doc.Pages)
            {
                var pageLines = lines.Where(x => x.PageIndex == pageIndex).ToArray();
                pageIndex++;
                if (pageLines.Length == 0)
                {
                    //没有需要替换的内容
                    continue;
                }
                var contentIndex = 0;
                foreach (var content in page.Contents)
                {
                    var contentLines = pageLines.Where(x => x.ContentIndex == contentIndex).ToArray();
                    contentIndex++;
                    if (contentLines.Length == 0)
                    {
                        //没有需要替换的内容
                        continue;
                    }
                    var lineIndex = 0;
                    var str = Encoding.UTF8.GetString(content.Stream.Value);
                    var reader = new StringReader(str);
                    using var writer = new StringWriter();
                    string? line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        var replaceTests = contentLines.Where(x => x.LineIndex == lineIndex).Select(x => x.Text).ToArray();
                        lineIndex++;
                        if (replaceTests.Length > 0)
                        {
                            //替换内容
                            if (!NoProblemAndWriteString(replaceTests, writer))
                            {
                                perfect = false;
                            }
                        }
                        else
                        {
                            //保留原内容或替换为空
                            if (!NoProblemAndWriteString([line], writer))
                            {
                                perfect = false;
                            }
                        }
                    }
                    var strResult = writer.ToString();
                    var bytesResult = Encoding.UTF8.GetBytes(strResult);
                    //无法直接创建Stream，临时建一个PdfDictionary来辅助
                    var tempDict = new PdfSharp.Pdf.PdfDictionary();
                    tempDict.CreateStream(bytesResult);
                    content.Stream = tempDict.Stream;
                }
            }
            doc.Save(outFilePath);
            return perfect;
        }
        Lazy<IEnumerable<FilePageContentLine>> contentLines;
        public IEnumerable<FilePageContentLine> ContentLines { get => contentLines.Value; }
        /// <summary>
        /// 得到有差别的行
        /// </summary>
        /// <param name="fileA"></param>
        /// <param name="fileB"></param>
        /// <returns></returns>
        public static IEnumerable<PageContentLine> GetDefferentLines(string fileA, string fileB)
        {
            static bool HasProblemWithString(string input)
            {
                return input.Contains(demo) || input.Contains(demoVersion1) || input.Contains(demoVersion2);
            }
            var helperA = new PdfStreamContentsHelper(fileA);
            var helperB = new PdfStreamContentsHelper(fileB);
            var set = new HashSet<PageContentLine>();
            set.UnionWith(helperA.ContentLines);
            set.SymmetricExceptWith(helperB.ContentLines);
            //有Demo类似字样的列表
            var problemList = helperA.ContentLines.Where(x => HasProblemWithString(x.Text)).ToArray();
            set.UnionWith(problemList);
            return set;
        }
    }

    class PageContentLine : IEquatable<PageContentLine>
    {
        public int PageIndex { get; set; }
        public int ContentIndex { get; set; }
        public int LineIndex { get; set; }
        public required string Text { get; set; }

        public bool Equals(PageContentLine? other)
        {
            var x = this; var y = other;
            if (y == null) return false;
            if (x.PageIndex != y.PageIndex) return false;
            if (x.LineIndex != y.LineIndex) return false;
            if (x.ContentIndex != y.ContentIndex) return false;
            if (x.Text != y.Text) return false;
            return true;
        }

        public override bool Equals(object? obj)
        {
            return Equals(obj as PageContentLine);
        }

        public override int GetHashCode()
        {
            return PageIndex.GetHashCode() + ContentIndex.GetHashCode() + LineIndex.GetHashCode() + Text.GetHashCode();
        }
    }
    class FilePageContentLine : PageContentLine
    {
        public required string FilePath { get; set; }
    }
}
